package com.ygy.utils;

import com.opencsv.CSVReader;
import com.ygy.marketdata.KLine;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.FileReader;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.LocalDate;
import java.time.YearMonth;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;

public class KLineLoader {
    private static final Logger log = LoggerFactory.getLogger(KLineLoader.class);

    // 定义外部数据存储的基础路径
    private static final String BASE_DATA_PATH = "F:/personal/binance_klines";

    /**
     * 根据指定的交易对、K线周期和时间范围，从外部文件系统加载K线数据。
     *
     * @param symbol    交易对, e.g., "ETHUSDT"
     * @param interval  K线周期, e.g., "1m", "1d"
     * @param startDate 开始日期
     * @param endDate   结束日期
     * @return KLine对象列表
     */
    public static List<KLine> loadKLines(String symbol, String interval, LocalDate startDate, LocalDate endDate) {
        List<KLine> allKLines = new ArrayList<>();
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM");

        YearMonth startMonth = YearMonth.from(startDate);
        YearMonth endMonth = YearMonth.from(endDate);

        // 构建数据所在的目录路径
        // 例如: F:/personal/binance_klines/ETHUSDT/1m
        Path dataDirectory = Paths.get(BASE_DATA_PATH, symbol, interval);

        if (!Files.exists(dataDirectory) || !Files.isDirectory(dataDirectory)) {
            log.error("数据目录不存在: {}", dataDirectory);
            return Collections.emptyList();
        }

        log.info("开始从目录加载K线数据: {}", dataDirectory);

        // 遍历指定时间范围内的每个月份
        for (YearMonth currentMonth = startMonth; !currentMonth.isAfter(endMonth); currentMonth = currentMonth.plusMonths(1)) {
            // 拼接文件名
            // 例如: ETHUSDT-1m-2020-11.csv
            String fileName = String.format("%s-%s-%s.csv", symbol, interval, currentMonth.format(formatter));
            Path filePath = dataDirectory.resolve(fileName);

            // 从单个CSV文件加载数据
            allKLines.addAll(loadFromSingleCsv(filePath));
        }

        // 按开盘时间升序排序，确保数据连续性
        allKLines.sort(Comparator.comparing(KLine::getOpenTime));

        log.info("总共加载了 {} 条K线数据，时间范围从 {} 到 {}", allKLines.size(), startDate, endDate);
        return allKLines;
    }

    /**
     * 从单个外部CSV文件加载K线数据。
     *
     * @param filePath CSV文件的完整路径
     * @return KLine对象列表
     */
    private static List<KLine> loadFromSingleCsv(Path filePath) {
        if (!Files.exists(filePath)) {
            // 文件不存在是正常情况（例如某个月份没有数据），使用warn级别日志
            log.warn("数据文件未找到，跳过: {}", filePath);
            return Collections.emptyList();
        }

        List<KLine> kLines = new ArrayList<>();
        // 使用 try-with-resources 确保资源被自动关闭
        try (FileReader fileReader = new FileReader(filePath.toFile());
             CSVReader csvReader = new CSVReader(fileReader)) {

            // 检查并跳过表头
            String[] firstLine = csvReader.peek();
            if (isHeader(firstLine)) {
                csvReader.readNext(); // 读取并丢弃表头行
            }

            // 读取所有剩余行
            List<String[]> lines = csvReader.readAll();
            kLines = lines.stream()
                    // 确保行数据足够长，避免ArrayIndexOutOfBoundsException
                    .filter(line -> line.length >= 12)
                    .map(line -> {
                        String volumeDelta = (line.length > 12) ? line[12] : "0";
                        return new KLine(Long.parseLong(line[0]), line[1], line[2], line[3], line[4], line[5], volumeDelta);
                    })
                    .collect(Collectors.toList());

            log.info("成功从文件 {} 加载了 {} 条K线。", filePath.getFileName(), kLines.size());

        } catch (IOException e) {
            log.error("读取CSV文件时发生IO错误: {}", filePath, e);
        } catch (Exception e) {
            log.error("解析CSV文件时发生错误: {}", filePath, e);
        }
        return kLines;
    }

    /**
     * 判断给定行是否为表头。
     * 策略是尝试将第一个元素解析为long，如果失败，则认为是表头。
     *
     * @param line CSV文件中的一行数据
     * @return 如果是表头则返回true，否则返回false
     */
    private static boolean isHeader(String[] line) {
        if (line == null || line.length == 0) {
            return false;
        }
        try {
            // 如果第一个字段可以被解析为数字，那么它就是数据行
            Long.parseLong(line[0]);
            return false;
        } catch (NumberFormatException e) {
            // 如果解析失败，那么它就是表头行（如 "open_time"）
            return true;
        }
    }
}